import { AutoMessageBean, AutoMessageId, ReceivedMessageBean, SendMessageBean } from "./MessageBaseBean";
import { MessageId } from "./MessageId";
import { Tools } from "../util/Tools";
import { Singleton } from "../../meshTools/Singleton";
import { EventType } from "../common/EventCenter";
import { GameMgr } from "../common/GameMgr";

export enum WebSocketToolState {
    Connecting = 'connecting', //连接中
    Connected = 'connected', //连接成功
    Closing = 'closing', //关闭
    Reconnecting = 'reconnecting',//重连中
    Timeout = 'Timeout',//链接超时
}


export class WebSocketTool extends Singleton {
    private ws: WebSocket;
    private webState: WebSocketToolState = WebSocketToolState.Closing;//默认是关闭的
    private reconnectInterval: number = 5000; // 重连间隔，单位毫秒
    private heartbeatInterval: number = 5000; // 心跳间隔，单位毫秒
    private heartbeatTimer: number = null; //心跳定时器的 id
    private reconnectMaxCount: number = 5; // 最大重连次数
    private reconnectCount: number = 0; // 当前重连次数
    private activeShutdown: boolean;//是不是主动关闭的
    private heartTimeOutTimer: number = null; //心跳超时定时器 id
    private heartTimeOutInterval: number = 11000; //心跳超时间隔，单位毫秒

    private msgFunction: Function = null;//这个是回调消息的的
    private stateFunction: Function = null;//这个是回调 socket 状态的

    public setSocketFunction(msgFunction: Function, stateFunction: Function) {
        this.msgFunction = msgFunction;
        this.stateFunction = stateFunction;
    }

    //连接
    public connect(): void {
        this.activeShutdown = false;
        if (this.webState == WebSocketToolState.Connecting || this.webState == WebSocketToolState.Connected) return;
        let url: string = GameMgr.GameData.ServerUrl;
        this.setWebState(WebSocketToolState.Connecting);
        this.ws = new WebSocket(url);
        GameMgr.Console.Log('webSocket 链接地址' + url);
        //连接成功
        this.ws.onopen = () => {
            this.setWebState(WebSocketToolState.Connected);
            // 连接成功后发送心跳包
            this.sendHeartbeat();
            this.sendHeartTimeOut()
            this.reconnectCount = 0;//连接上之后 重置重连次数
        };

        //收到消息
        this.ws.onmessage = (event) => {
            // 处理接收到的消息
            GameMgr.Console.Log(`webSocket 收到消息(${Tools.getCurrentTimeWithMilliseconds()}):${event.data}`);
            const parsedData = JSON.parse(event.data) as ReceivedMessageBean;
            if (parsedData.msgId == MessageId.MsgTypeHeartbeat) {
                this.sendHeartTimeOut()
            }


            if (this.msgFunction) {
                this.msgFunction(event.data);
            }
        };

        //关闭连接
        this.ws.onclose = () => {
            if (this.webState == WebSocketToolState.Reconnecting) {
                //在 ios 系统中 断网不会断开 websocket，所以在这状态下收到的 链接关闭都是上一个的
                console.error('WebSocket error:', '旧链接关闭');
                return
            }

            console.error('WebSocket error:', '连接关闭');
            this.setWebState(WebSocketToolState.Closing);
            this.linkException()
        };

        //连接异常
        this.ws.onerror = (event) => {
            console.error('WebSocket error:', event);
        };
    }

    //主动断开连接
    public disconnect(): void {
        console.error('WebSocket error:', '主动断开');
        this.activeShutdown = true;
        this.ws.close();
    }


    //重连，内部自动调用的
    private reconnect(): void {
        if (this.webState == WebSocketToolState.Reconnecting || this.webState == WebSocketToolState.Connecting) return;

        if (this.reconnectCount >= this.reconnectMaxCount) {
            console.error('WebSocket 重连失败');
            let autoMessageBean: AutoMessageBean = {
                'msgId': AutoMessageId.ReconnectionFailureMsg,//长链接重连失败
                'data': {}
            }
            GameMgr.Event.Send(EventType.AutoMessage, autoMessageBean);
            return;
        }
        this.reconnectCount++;
        this.setWebState(WebSocketToolState.Reconnecting);

        // 重连间隔后重新连接
        setTimeout(() => {
            this.connect();
        }, this.reconnectInterval);
    }

    //给外部调用的马上重连
    public atOnceReconnect() {
        this.connect();
    }

    //启动心跳
    private sendHeartbeat(): void {
        if (this.webState != WebSocketToolState.Connected) return;

        let sendMessageBean: SendMessageBean = {
            msgId: MessageId.MsgTypeHeartbeat,
            data: {},
        };
        const jsonString = JSON.stringify(sendMessageBean);

        // 发送心跳包
        this.send(jsonString);

        // 每隔心跳间隔发送一次心跳包
        this.heartbeatTimer = setTimeout(() => {
            this.sendHeartbeat();
        }, this.heartbeatInterval);
    }


    private sendHeartTimeOut(): void {
        this.stopHeartTime();
        this.heartTimeOutTimer = setTimeout(() => {
            if (this.webState == WebSocketToolState.Connected) {
                console.error('WebSocket 连接超时');
                this.setWebState(WebSocketToolState.Timeout);
                console.error('WebSocket 连接超时  强制关闭长链接');
                this.ws.close(); // Todo 强制关闭一下长链接，不然ios断网不会断开
                this.linkException()
            }

        }, this.heartTimeOutInterval); // 11 秒心跳超时
    }

    //链接异常
    linkException() {
        this.stopHeartbeat();
        this.stopHeartTime();
        if (!this.activeShutdown) {
            let autoMessageBean: AutoMessageBean = {
                'msgId': AutoMessageId.LinkExceptionMsg,//长链接异常
                'data': {}
            }
            GameMgr.Event.Send(EventType.AutoMessage, autoMessageBean);
            // 连接关闭后尝试重连
            this.reconnect();
        }
    }


    //停止心跳
    private stopHeartbeat() {
        if (this.heartbeatTimer) {
            // 清除心跳定时器
            clearTimeout(this.heartbeatTimer);
            this.heartbeatTimer = null;
        }

    }

    //停止超时监听
    private stopHeartTime() {
        if (this.heartTimeOutTimer) {
            clearTimeout(this.heartTimeOutTimer);
            this.heartTimeOutTimer = null
        }
    }

    //发送消息
    public send(message: string): void {
        GameMgr.Console.Log(`webSocket 发送消息(${Tools.getCurrentTimeWithMilliseconds()}):${message}`);
        if (this.webState == WebSocketToolState.Connected) {
            this.ws.send(message);
        } else {
            GameMgr.Console.Error('WebSocket is not connected');
        }
    }

    //赋值 websocket 状态
    setWebState(webState: WebSocketToolState) {
        this.webState = webState;
        this.stateFunction(webState)
    }

    onDestroy() {
        // 清除心跳定时器
        this.stopHeartbeat();
        this.stopHeartTime();
    }
}